﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ModelLayer.Graph
{
    public class GraphCreator
    {


        private static readonly string APP_KEY = "Fmjtd%7Cluurnuut21%2C8a%3Do5-9wr01u";
        private static readonly string BASE_ADDRESS = "http://open.mapquestapi.com/directions/v2/routematrix";
        private static readonly string REQUEST_PARAMS = "?key=" + APP_KEY;
        private static readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1);
        private static readonly int RETRIES = 3;

        public async Task<StationNetworkGraph> CreateGraph(IList<Station> stations, double[] startCoords, double[] endCoords, BatteryType desired_type)
        {
            using (HttpClient client = new HttpClient())
            {
                JsonRequestRoot jrr = MakeJsonRequestObject(stations, startCoords, endCoords);

                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.BaseAddress = new Uri(BASE_ADDRESS);

                try
                {
                    using (HttpResponseMessage response = await client.PostAsJsonAsync(REQUEST_PARAMS, jrr))
                    {
                        for (int i = 0; i < RETRIES; i++)
                        {
                            if (response.IsSuccessStatusCode)
                            {
                                string jsonResponse = await response.Content.ReadAsStringAsync();
                                dynamic data = JObject.Parse(jsonResponse);

                                JArray distances = data.distance;
                                JArray times = data.time;
                                int amount = distances.Count;

                                double[][] distanceMatrix = new double[amount][];
                                double[][] timeMatrix = new double[amount][];

                                if (amount == times.Count)
                                {
                                    for (int j = 0; j < amount; j++)
                                    {
                                        distanceMatrix[j] = JsonConvert.DeserializeObject<double[]>(distances[j].ToString());
                                        timeMatrix[j] = JsonConvert.DeserializeObject<double[]>(times[j].ToString());
                                    }
                                }
                                else
                                {
                                    throw new Exception("The API stopped providing good data, times and distances have different amounts");
                                }

                                stations.Add(null); //add for the start node
                                stations.Add(null); //add for end node

                                StationNetworkGraph graph = new StationNetworkGraph(desired_type);
                                graph.SetMultipleNodes(stations, distanceMatrix, timeMatrix);
                                return graph;
                            }
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
            return null;
        }





        private JsonRequestRoot MakeJsonRequestObject(IList<Station> stations, double[] startCoords, double[] endCoords)
        {
            JsonRequestRoot jrr = new JsonRequestRoot
            {
                locations = new List<Location>(),
                options = new Options() { allToAll = true, unit = "k" }
            };

            List<Location> locations = jrr.locations;

            
            foreach (Station station in stations)
            {
                locations.Add(new Location() { latLng = new LatLng() { lat = station.latitude, lng = station.longitude } });
            }
            locations.Add(new Location() { latLng = new LatLng() { lat = startCoords[0], lng = startCoords[1] } });
            locations.Add(new Location() { latLng = new LatLng() { lat = endCoords[0], lng = endCoords[1] } });

            return jrr;
        }

    }

    class LatLng
    {
        public double lat { get; set; }
        public double lng { get; set; }
    }

    class Location
    {
        public LatLng latLng { get; set; }
    }

    class Options
    {
        public bool allToAll { get; set; }
        public string unit { get; set; }
    }

    class JsonRequestRoot
    {
        public List<Location> locations { get; set; }
        public Options options { get; set; }
    }
}
